/*
 * BitMap_test.cpp
 *
 *  Created on: Nov 19, 2012
 *      Author: m
 */

#include <gtest/gtest.h>
#include <memory>
#include "../physical_layer/common_components/BitMapFreeSpaceManager.h"

using physical::byte;
using physical::BitMapFreeSpaceManager;

/**
 * Test Suite de la clase BitMapFreeSpaceManager
 */
class BitMapFreeSpaceManagerTestFixture : public ::testing::Test
{
public:
    BitMapFreeSpaceManagerTestFixture()
    {
    }

    virtual ~BitMapFreeSpaceManagerTestFixture()
    {
    }

    void SetUp()
    {
    }

    void TearDown()
    {
    }
};

/**
 * Test del constructor por defecto
 *
 * Se espera que se cree un mapa de bits con todas sus posiciones libres.
 */
TEST_F( BitMapFreeSpaceManagerTestFixture, InitializationByDefault )
{
    size_t mapSize = 128;
    BitMapFreeSpaceManager bitmap( mapSize );

    // Verifico que el tamaño del bitmap sea el esperado
    ASSERT_EQ( bitmap.getSize(), mapSize );

    // Verifico que todas las posiciones del mapa están vacias
    for ( unsigned int i = 0; i < mapSize * 8; i++ )
        ASSERT_TRUE( bitmap.isFree(i) );
}

/**
 * Test del constructor por valor
 *
 * Se espera que se cree un mapa de bits inicializado en el valor dado.
 */
TEST_F( BitMapFreeSpaceManagerTestFixture, InitializationByValue )
{
    size_t mapSize = 10;
    byte initializationValue[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    BitMapFreeSpaceManager bitmap( mapSize, initializationValue );

    // Verifico que el mapa se haya creado con el valor provisto
    ASSERT_EQ( memcmp( bitmap.get(), &initializationValue, mapSize ), 0 );
}

/**
 * Test de control de espacio libre
 *
 * Se crea un mapa con todas sus posiciones libres, luego
 *  se marcan algunas como ocupadas y finalmente verifico.
 */
TEST_F( BitMapFreeSpaceManagerTestFixture, set_unset_freeSpace )
{
    size_t mapSize = 32;
    BitMapFreeSpaceManager bitmap( mapSize );

    // Creo una lista de posiciones ocupadas
    std::set<int> occupedPositions;

    occupedPositions.insert( 1 );
    occupedPositions.insert( 10 );
    occupedPositions.insert( 15 );
    occupedPositions.insert( 17 );
    occupedPositions.insert( 22 );
    occupedPositions.insert( 50 );
    occupedPositions.insert( 79 );
    occupedPositions.insert( 152 );
    occupedPositions.insert( 200 );
    occupedPositions.insert( 255 );

    // Marco esas posiciones como ocupadas en el mapa
    for( auto it = occupedPositions.begin(); it != occupedPositions.end(); it++ )
        bitmap.unfree( *it );

    // Verifico que solo esas posiciones estén marcadas como ocupadas
    for ( unsigned int i = 0; i < mapSize * 8; i++ )
    {
        if ( occupedPositions.find( i ) != occupedPositions.end() )
        {
            ASSERT_FALSE( bitmap.isFree( i ) );
        }
        else
        {
            ASSERT_TRUE( bitmap.isFree( i ) );
        }
    }
}

/**
 * Test de reutilización de espacio libre
 *
 *
 */
TEST_F( BitMapFreeSpaceManagerTestFixture, freeSpaceReutilization )
{
    // Se crea un mapa totalmente ocupado
    size_t mapSize = 32;
    byte initializationValue[mapSize];
    memset( initializationValue, 0xFF, mapSize );
    BitMapFreeSpaceManager bitmap( mapSize, initializationValue );

    // Creo una lista de posiciones vacias
    std::set<int> freePositions;

    freePositions.insert( 1 );
    freePositions.insert( 10 );
    freePositions.insert( 15 );
    freePositions.insert( 17 );
    freePositions.insert( 22 );
    freePositions.insert( 50 );
    freePositions.insert( 79 );
    freePositions.insert( 152 );
    freePositions.insert( 200 );
    freePositions.insert( 255 );

    // Marco esas posiciones como libres en el mapa
    for( auto it = freePositions.begin(); it != freePositions.end(); it++ )
        bitmap.free( *it );

    // Voy pidiendo posiciones vacias de a una, verifico
    //  que sean las esperadas y las marco como ocupadas
    auto freePositionsCount = freePositions.size();

    for ( unsigned int i = 0; i < freePositionsCount; i++ )
    {
        auto freePosition = bitmap.getFreeBlockNumber();

        // Verifico que la posición devuelta pertenezca a la lista de posiciones libres
        auto it = freePositions.find( freePosition );
        ASSERT_TRUE( it != freePositions.end() );

        // La marco como ocupada y la saco de la lista
        bitmap.unfree( freePosition );
        freePositions.erase( it );
    }

    // Verifico que lanze una excepción si no quedan bloques libres y pido uno
    ASSERT_THROW( bitmap.getFreeBlockNumber(), physical::NoMoreFreeBlocksException);
}
